﻿using ShiQuan.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;

namespace ShiQuan.Redis
{
    /// <summary>
    /// redis允许客户端以TCP方式连接，默认6379端口。传输数据都以\r\n结尾。
    /// </summary>
    public class RedisClient : IDisposable
    {
        /// <summary>
        /// 请求头部格式， *<number of arguments>\r\n
        /// </summary>
        public const string Header = "*{0}\r\n";
        /// <summary>
        /// 参数信息$<number of bytes of argument N>\r\n<argument data>\r\n
        /// </summary>
        public const string Bulk = "${0}\r\n{1}\r\n";

        //通信socket
        private Socket _Socket;

        public string IP { get; private set; }

        public int Port { get; private set; }

        private bool _NoDelay = false;
        /// <summary>
        /// Socket 是否正在使用 Nagle 算法。
        /// </summary>
        public bool NoDelay {
            get { return this._NoDelay; }
            set { this._NoDelay = value; }
        }

        public RedisClient(string ip,int port=6379)
        {
            this.IP = ip;
            this.Port = port;
        }
        /// <summary>
        /// 连接
        /// </summary>
        /// <returns></returns>
        public bool Connect(bool connection = false)
        {
            /*2020-05-24 侯连文 是否重新建立连接*/
            if(connection == false)
            {
                if (_Socket != null && _Socket.Connected)
                    return true;
            }
            _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            {
                NoDelay = this.NoDelay
            };
            this._Socket.ReceiveTimeout = 30 * 1000;//超时
            this._Socket.Connect(this.IP, this.Port);
            return this._Socket.Connected;
        }

        public string Set(string key,string value)
        {
            return this.Send(RedisCommand.SET, key,value);
        }

        public string Set<T>(string key, T value) where T : class
        {
            var result = JsonConvert.ToJson<T>(value);
            return this.Send(RedisCommand.SET, key, result);
        }

        public string Set(params string[] args)
        {
            return this.Send(RedisCommand.SET, args);
        }

        public string Get(string key)
        {
            return this.Send(RedisCommand.GET, key);
        }

        public T Get<T>(string key) where T : class
        {
            var result = this.Send(RedisCommand.GET, key);
            return JsonConvert.ToObject<T>(result);
        }

        public string Get(params string[] args)
        {
            return this.Send(RedisCommand.GET, args);
        }

        public string MULTI()
        {
            return this.Send(RedisCommand.MULTI);
        }

        public string EXEC()
        {
            return this.Send(RedisCommand.EXEC);
        }

        public string DISCARD()
        {
            return this.Send(RedisCommand.DISCARD);
        }

        public string QUIT()
        {
            return this.Send(RedisCommand.QUIT);
        }

        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string Send(string[] args)
        {
            var builder = new StringBuilder();
            builder.AppendFormat(RedisClient.Header, args.Length);
            foreach (var arg in args)
            {
                builder.AppendFormat(RedisClient.Bulk, arg.Length, arg);
            }
            ThreadLogHelper.Info("发送内容", builder.ToString());
            //Console.WriteLine("发送内容：" + builder.ToString());
            return this.Send(builder.ToString());
        }
        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string Send(string command, params string[] args)
        {
            var builder = new StringBuilder();
            builder.AppendFormat(RedisClient.Header, args.Length + 1);

            var cmd = command.ToString();
            builder.AppendFormat(RedisClient.Bulk, cmd.Length, cmd);

            foreach (var arg in args)
            {
                builder.AppendFormat(RedisClient.Bulk, arg.Length, arg);
            }
            ThreadLogHelper.Info("发送内容", builder.ToString());
            //Console.WriteLine("发送内容：" + builder.ToString());
            return this.Send(builder.ToString());
        }
        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string Send(RedisCommand command, params string[] args)
        {
            var builder = new StringBuilder();
            builder.AppendFormat(RedisClient.Header, args.Length + 1);

            var cmd = command.ToString();
            builder.AppendFormat(RedisClient.Bulk, cmd.Length, cmd);

            foreach (var arg in args)
            {
                builder.AppendFormat(RedisClient.Bulk, arg.Length, arg);
            }
            ThreadLogHelper.Info("发送内容", builder.ToString());
            //Console.WriteLine("发送内容：" + builder.ToString());
            return this.Send(builder.ToString());
        }
        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string Send(string content)
        {
            try
            {
                this.Connect();

                this._Socket.Send(Encoding.UTF8.GetBytes(content));
                if (content == RedisCommand.QUIT)
                    return string.Empty;
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("发送信息异常", ex);
                Console.WriteLine("发送信息异常：" + ex.Message);
                return string.Empty;
            }
            return this.Receive();
        }
        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="command"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string Send(byte[] bytes)
        {
            try
            {
                this.Connect();

                this._Socket.Send(bytes);
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("发送信息异常",ex);
                Console.WriteLine("发送信息异常：" + ex.Message);
                return string.Empty;
            }
            return this.Receive();
        }
        /// <summary>
        /// 响应内容
        /// </summary>
        /// <returns></returns>
        private string Receive()
        {
            try
            {
                byte[] buffer = new byte[1];
                var count = this._Socket.Receive(buffer);
                var result = Encoding.UTF8.GetString(buffer, 0, count);
                ThreadLogHelper.Info("首字符：", result);
                if (result == "-")//异常处理。
                {
                    return this.ReadLine();
                }
                if(result == "+")//状态回复。
                {
                    return this.ReadLine();
                }
                if (result == "$")//内容块
                {
                    result = this.ReadLine();//内容长度
                    ThreadLogHelper.Info("首行：",result);
                    if (result == "-1")
                        return result;

                    return this.ReadData(int.Parse(result));
                }
                if(result == "*")
                {
                    result = this.ReadLine();//内容长度
                    ThreadLogHelper.Info("首行：", result);
                    if (result == "-1")
                        return result;
                    return this.ReadChunk(int.Parse(result));
                }
                return this.ReadLine();
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("解释响应内容异常：", ex);
                Console.WriteLine("解释响应内容异常：" + ex.Message);
                return string.Empty;
            }
        }
        /// <summary>
        /// 读取一行数据
        /// </summary>
        /// <returns></returns>
        private string ReadLine()
        {
            StringBuilder builder = new StringBuilder();
            byte[] buffer = new byte[1];
            do
            {
                this._Socket.Receive(buffer,SocketFlags.None);
                var data = Encoding.UTF8.GetString(buffer);
                if (data == "\r")
                    continue;
                if (data == "\n")
                    break;
                builder.Append(data);
            } while (true);
            return builder.ToString();
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="total"></param>
        /// <returns></returns>
        private string ReadChunk(int total)
        {
            if (total <= 0)
                return string.Empty;

            //int amount = int.Parse(result);
            int start = 0, count = 0;
            StringBuilder builder = new StringBuilder();
            do
            {
                start++;

                byte[] buffer = new byte[1];
                var line = this.ReadLine() ;
                count = Convert.ToInt32(line.Replace("$", ""));
                if(count == -1)
                {
                    builder.AppendLine("-1");
                    continue;
                }
                builder.AppendLine(this.ReadData(count));

            } while (start <= total);

            return builder.ToString();
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="total"></param>
        /// <returns></returns>
        private string ReadData(int total)
        {
            if (total <= 0)
                return string.Empty;

            //int amount = int.Parse(result);
            int start = 0,count = 0;
            List<byte> list = new List<byte>();
            do
            {
                byte[] buffer = new byte[1024];
                count = this._Socket.Receive(buffer);
                if (count > 0)
                {
                    byte[] temp = new byte[count];
                    System.Buffer.BlockCopy(buffer, 0, temp, 0, count);
                    list.AddRange(temp);
                }
                start += count;
            } while (start <= total);

            return Encoding.UTF8.GetString(list.ToArray(), 0, list.Count);
        }
        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            bool status = false;
            if (this._Socket != null)
                status = this._Socket.Connected;

            try
            {
                if(status)
                    Send(RedisCommand.QUIT);
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("关闭连接异常", ex);
                Console.WriteLine("关闭连接异常:" + ex.Message);
            }
            try
            {
                if(status)
                    this._Socket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("关闭连接异常", ex);
                Console.WriteLine("关闭连接异常:" + ex.Message);
            }
            try
            {
                if (this._Socket != null)
                {
                    _Socket.Disconnect(false);
                    _Socket.Close();
                }
            }
            catch (Exception ex)
            {
                ThreadLogHelper.Error("关闭连接异常", ex);
                Console.WriteLine("关闭连接异常:" + ex.Message);
            }
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            this.Close();
        }
    }
}
